14  적합성 및 독립성 검정

Keywords

python, 전처리, 통계, 가설검정, 기계학습, 회귀, 분류, 군집, 모델 학습, 모델 평가

적합성 검정(Goodness-of-Fit Test)과 독립성 검정(Test of Independence)은 범주형 데이터의 빈도와 구조를 분석하는 방법이다. 이 검정들은 평균이 아닌 관측 빈도와 기대 빈도의 차이를 통해 데이터의 패턴을 파악한다. 적합성 검정은 “데이터가 특정 이론적 분포를 따르는가?”를, 독립성 검정은 “두 범주형 변수가 서로 관련이 있는가?”를 확인한다. 이 장에서는 카이제곱 검정을 중심으로 범주형 데이터 분석 방법을 학습한다.

예제: 데이터 로드

import seaborn as sns
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from scipy.stats import chisquare, chi2_contingency, f

# 데이터 로드
df = sns.load_dataset("penguins").dropna()

print("데이터 크기:", df.shape)
print("\n범주형 변수:")
print(df.select_dtypes(include=['object', 'category']).columns.tolist())
데이터 크기: (333, 7)

범주형 변수:
['species', 'island', 'sex']

14.1 검정 유형과 적용 시점

적합성 검정과 독립성 검정은 다음과 같은 질문에 답한다.

검정 유형별 질문

검정 유형 핵심 질문 변수 개수 비교 대상
적합성 검정 이 데이터는 내가 기대한 분포를 따르는가? 1개 관측 분포 vs 이론 분포
독립성 검정 두 범주형 변수는 서로 관련이 있는가? 2개 변수 간 연관성
동질성 검정 여러 집단의 분포가 같은가? 2개 집단 간 분포

이러한 검정은 평균이 아니라 빈도와 구조를 파악하기 위해 수행하며, t-검정이나 ANOVA와는 다른 목적으로 사용된다.

검정 적용 시점

  • 범주형 변수의 분포를 이론적 분포와 비교할 때
  • 두 범주형 변수 간 관계를 탐색할 때
  • 샘플링이 모집단을 대표하는지 확인할 때
  • 실험 설계의 균형성을 검증할 때

14.2 카이제곱(χ²) 검정의 기본 원리

모든 카이제곱 검정은 관측 빈도와 기대 빈도의 차이를 측정한다.

카이제곱 검정 통계량

\[ \chi^2 = \sum_{i=1}^{k} \frac{(O_i - E_i)^2}{E_i} \]

여기서: - \(O_i\): 관측 빈도(Observed frequency) - \(E_i\): 기대 빈도(Expected frequency) - \(k\): 범주의 개수

검정 통계량의 의미

χ² 값 의미 결론
0에 가까움 관측값 ≈ 기대값 귀무가설 지지
관측값 ≠ 기대값 대립가설 지지

χ² 값이 클수록 관측 데이터가 기대 분포에서 벗어난 정도가 크다.

14.3 적합성 검정 (Goodness-of-Fit Test)

적합성 검정은 관측된 범주 빈도가 미리 정해진 이론적 분포와 일치하는지 확인하는 검정이다.

가설 설정

  • H₀ (귀무가설): 관측 분포 = 기대 분포 (데이터가 이론 분포를 따름)
  • H₁ (대립가설): 관측 분포 ≠ 기대 분포 (데이터가 이론 분포를 따르지 않음)

14.3.1 균등 분포 검정

가장 기본적인 적합성 검정은 모든 범주가 동일한 비율로 나타나는지 확인하는 것이다.

예제: 종 분포가 균등한가?

# 종별 관측 빈도
observed = df["species"].value_counts().sort_index()

print("=== 관측 빈도 ===")
print(observed)
print(f"\n총 개체 수: {observed.sum()}")

# 균등 분포 기대 빈도
expected = [observed.sum() / len(observed)] * len(observed)

print("\n=== 기대 빈도 (균등 분포) ===")
for species, exp in zip(observed.index, expected):
    print(f"{species}: {exp:.2f}")
=== 관측 빈도 ===
species
Adelie       146
Chinstrap     68
Gentoo       119
Name: count, dtype: int64

총 개체 수: 333

=== 기대 빈도 (균등 분포) ===
Adelie: 111.00
Chinstrap: 111.00
Gentoo: 111.00

예제: 카이제곱 적합성 검정

# 적합성 검정
stat, p_value = chisquare(f_obs=observed, f_exp=expected)

print("\n=== 카이제곱 적합성 검정 ===")
print(f"χ² 통계량: {stat:.4f}")
print(f"p-value: {p_value:.4f}")
print(f"\n유의수준 0.05 기준: ", end="")
if p_value > 0.05:
    print("균등 분포를 따른다고 볼 수 있음 (H₀ 채택)")
else:
    print("균등 분포가 아님 (H₀ 기각)")

# 시각화
fig, axes = plt.subplots(1, 2, figsize=(14, 5))

# 관측 빈도
axes[0].bar(observed.index, observed.values, alpha=0.7, edgecolor='black')
axes[0].set_title("Observed Frequencies")
axes[0].set_xlabel("Species")
axes[0].set_ylabel("Count")

# 관측 vs 기대 비교
x = np.arange(len(observed))
width = 0.35
axes[1].bar(x - width/2, observed.values, width, label='Observed', alpha=0.7)
axes[1].bar(x + width/2, expected, width, label='Expected (Uniform)', alpha=0.7)
axes[1].set_xlabel("Species")
axes[1].set_ylabel("Count")
axes[1].set_title("Observed vs Expected")
axes[1].set_xticks(x)
axes[1].set_xticklabels(observed.index)
axes[1].legend()

plt.tight_layout()
plt.show()

=== 카이제곱 적합성 검정 ===
χ² 통계량: 28.2703
p-value: 0.0000

유의수준 0.05 기준: 균등 분포가 아님 (H₀ 기각)

14.3.2 특정 비율 검정

이론적으로 특정 비율이 예상되는 경우, 그 비율과 관측 데이터를 비교할 수 있다.

예제: 특정 비율 검정

# 이론적 비율 설정 (예: 4:3:3)
theoretical_ratio = [4, 3, 3]
total = observed.sum()

# 비율에 맞는 기대 빈도 계산
expected_ratio = [total * r / sum(theoretical_ratio) for r in theoretical_ratio]

print("=== 특정 비율 검정 (4:3:3) ===")
print(f"기대 비율: {theoretical_ratio}")
print(f"\n기대 빈도:")
for species, exp in zip(observed.index, expected_ratio):
    print(f"{species}: {exp:.2f}")

# 검정
stat, p_value = chisquare(f_obs=observed, f_exp=expected_ratio)

print(f"\nχ² 통계량: {stat:.4f}")
print(f"p-value: {p_value:.4f}")
print(f"\n유의수준 0.05 기준: ", end="")
if p_value > 0.05:
    print("이론적 비율과 일치 (H₀ 채택)")
else:
    print("이론적 비율과 다름 (H₀ 기각)")
=== 특정 비율 검정 (4:3:3) ===
기대 비율: [4, 3, 3]

기대 빈도:
Adelie: 133.20
Chinstrap: 99.90
Gentoo: 99.90

χ² 통계량: 15.0681
p-value: 0.0005

유의수준 0.05 기준: 이론적 비율과 다름 (H₀ 기각)

14.3.3 적합성 검정의 활용 사례

적용 분야

분야 질문 예시
품질 관리 제품 불량률이 기준치인가? 불량품 5% 예상 vs 실제 관측
유전학 멘델의 법칙을 따르는가? 3:1 비율 검정
게임 주사위가 공정한가? 각 면이 1/6 확률
샘플링 표본이 모집단을 대표하는가? 성별/연령 분포 비교
마케팅 광고 효과가 균등한가? 요일별 방문자 수

14.4 독립성 검정 (Test of Independence)

독립성 검정은 두 범주형 변수가 서로 독립적인지, 즉 하나의 변수를 알았을 때 다른 변수의 분포가 변하는지를 확인하는 검정이다.

가설 설정

  • H₀ (귀무가설): 두 변수는 독립이다 (관련이 없다)
  • H₁ (대립가설): 두 변수는 독립이 아니다 (관련이 있다)

독립의 의미

두 변수 A와 B가 독립이라는 것은 다음을 의미한다.

\[ P(A \cap B) = P(A) \times P(B) \]

즉, A와 B가 동시에 일어날 확률이 각각의 확률의 곱과 같다.

14.4.1 교차표 (Contingency Table)

독립성 검정은 교차표를 기반으로 수행한다.

예제: 종과 성별의 교차표

# 교차표 생성
contingency_table = pd.crosstab(
    df["species"],
    df["sex"],
    margins=True  # 합계 포함
)

print("=== 교차표: 종 × 성별 ===")
print(contingency_table)

# 비율로 변환 (행 기준)
contingency_pct = pd.crosstab(
    df["species"],
    df["sex"],
    normalize='index'
) * 100

print("\n=== 종별 성별 비율 (%) ===")
print(contingency_pct.round(1))
=== 교차표: 종 × 성별 ===
sex        Female  Male  All
species                     
Adelie         73    73  146
Chinstrap      34    34   68
Gentoo         58    61  119
All           165   168  333

=== 종별 성별 비율 (%) ===
sex        Female  Male
species                
Adelie       50.0  50.0
Chinstrap    50.0  50.0
Gentoo       48.7  51.3

예제: 교차표 시각화

# 교차표 시각화 (합계 제외)
contingency_no_margin = pd.crosstab(df["species"], df["sex"])

# 히트맵
plt.figure(figsize=(8, 5))
sns.heatmap(contingency_no_margin, annot=True, fmt='d', cmap='Blues')
plt.title("Contingency Table: Species × Sex")
plt.xlabel("Sex")
plt.ylabel("Species")
plt.show()

# 그룹화 막대 그래프
contingency_no_margin.plot(kind='bar', figsize=(10, 5))
plt.title("Species and Sex Distribution")
plt.xlabel("Species")
plt.ylabel("Count")
plt.xticks(rotation=0)
plt.legend(title="Sex")
plt.tight_layout()
plt.show()

14.4.2 카이제곱 독립성 검정

예제: 독립성 검정 수행

# 독립성 검정 (합계 제외)
chi2, p_value, dof, expected_freq = chi2_contingency(contingency_no_margin)

print("=== 카이제곱 독립성 검정 ===")
print(f"χ² 통계량: {chi2:.4f}")
print(f"자유도(df): {dof}")
print(f"p-value: {p_value:.4f}")
print(f"\n유의수준 0.05 기준: ", end="")
if p_value > 0.05:
    print("두 변수는 독립이다 (관련 없음, H₀ 채택)")
else:
    print("두 변수는 독립이 아니다 (관련 있음, H₀ 기각)")
=== 카이제곱 독립성 검정 ===
χ² 통계량: 0.0486
자유도(df): 2
p-value: 0.9760

유의수준 0.05 기준: 두 변수는 독립이다 (관련 없음, H₀ 채택)

14.4.3 기대 빈도 확인

카이제곱 검정의 신뢰도는 기대 빈도에 의존한다.

예제: 기대 빈도 확인

# 기대 빈도 출력
expected_df = pd.DataFrame(
    expected_freq,
    index=contingency_no_margin.index,
    columns=contingency_no_margin.columns
)

print("\n=== 기대 빈도 ===")
print(expected_df.round(2))

# 기대 빈도 < 5인 셀 확인
low_expected = (expected_df < 5).sum().sum()
total_cells = expected_df.size

print(f"\n기대 빈도 < 5인 셀: {low_expected}/{total_cells}")

if low_expected > 0:
    print("⚠️ 주의: 기대 빈도가 5 미만인 셀이 있으면 카이제곱 검정 신뢰도 저하")
    print("   → 범주 통합 또는 Fisher의 정확 검정 고려")
else:
    print("✓ 모든 셀의 기대 빈도가 5 이상으로 검정 신뢰도 양호")

=== 기대 빈도 ===
sex        Female   Male
species                 
Adelie      72.34  73.66
Chinstrap   33.69  34.31
Gentoo      58.96  60.04

기대 빈도 < 5인 셀: 0/6
✓ 모든 셀의 기대 빈도가 5 이상으로 검정 신뢰도 양호

기대 빈도 조건

조건 권장 조치
모든 셀의 기대 빈도 ≥ 5 카이제곱 검정 사용 가능
20% 이상의 셀이 기대 빈도 < 5 범주 통합 또는 Fisher 정확 검정
2×2 표에서 기대 빈도 < 5 Yates 연속성 수정 또는 Fisher 정확 검정

14.4.4 연관성의 강도 측정

카이제곱 검정은 연관성의 존재 여부만 알려주므로, 연관성의 강도를 측정하려면 추가 지표가 필요하다.

예제: Cramér’s V 계산

# Cramér's V 계산
n = contingency_no_margin.sum().sum()
min_dim = min(contingency_no_margin.shape[0] - 1, contingency_no_margin.shape[1] - 1)
cramers_v = np.sqrt(chi2 / (n * min_dim))

print(f"\n=== 연관성 강도 ===")
print(f"Cramér's V: {cramers_v:.4f}")

# 해석
if cramers_v < 0.1:
    strength = "매우 약함"
elif cramers_v < 0.3:
    strength = "약함"
elif cramers_v < 0.5:
    strength = "중간"
else:
    strength = "강함"

print(f"연관성 강도: {strength}")

=== 연관성 강도 ===
Cramér's V: 0.0121
연관성 강도: 매우 약함

Cramér’s V 해석

Cramér’s V 연관성 강도
0.00 ~ 0.10 매우 약함
0.10 ~ 0.30 약함
0.30 ~ 0.50 중간
0.50 이상 강함

14.5 F 검정 (분산 비교)

F 검정은 두 집단의 분산이 같은지 확인하는 검정이다. 등분산성 검정의 가장 기본적인 형태이지만, 정규성에 매우 민감하여 실무에서는 Levene 검정을 더 많이 사용한다.

F 통계량

\[ F = \frac{s_1^2}{s_2^2} \]

여기서 \(s_1^2\)\(s_2^2\)는 각 집단의 표본 분산이다.

예제: F 검정

# 두 종 선택
group1 = df[df["species"] == "Adelie"]["body_mass_g"].dropna()
group2 = df[df["species"] == "Chinstrap"]["body_mass_g"].dropna()

# F 통계량 계산
f_stat = group1.var() / group2.var()

print("=== F 검정: Adelie vs Chinstrap ===")
print(f"\nAdelie 분산: {group1.var():.2f}")
print(f"Chinstrap 분산: {group2.var():.2f}")
print(f"\nF 통계량: {f_stat:.4f}")

# 자유도
df1 = len(group1) - 1
df2 = len(group2) - 1

# p-value 계산 (양측 검정)
p_value = 2 * min(f.cdf(f_stat, df1, df2), 1 - f.cdf(f_stat, df1, df2))

print(f"p-value: {p_value:.4f}")
print(f"\n유의수준 0.05 기준: ", end="")
if p_value > 0.05:
    print("등분산 (H₀ 채택)")
else:
    print("이분산 (H₀ 기각)")

print("\n⚠️ 주의: F 검정은 정규성에 매우 민감")
print("   → 실무에서는 Levene 검정 권장")
=== F 검정: Adelie vs Chinstrap ===

Adelie 분산: 210332.43
Chinstrap 분산: 147713.45

F 통계량: 1.4239
p-value: 0.1047

유의수준 0.05 기준: 등분산 (H₀ 채택)

⚠️ 주의: F 검정은 정규성에 매우 민감
   → 실무에서는 Levene 검정 권장

F 검정 vs Levene 검정

항목 F 검정 Levene 검정
가정 정규성 필요 정규성 불필요
강건성 정규성 위배 시 부정확 안정적
용도 이론 설명용 실무 권장

14.6 검정 방법 종합 비교

검정 유형별 정리

목적 데이터 유형 변수 개수 검정 방법 주요 사용
분포 적합성 범주형 1개 χ² 적합성 검정 이론 분포와 비교
변수 관계 범주형 × 범주형 2개 χ² 독립성 검정 범주 간 연관성
분산 비교 (이론) 연속형 2개 F 검정 교육 목적
분산 비교 (실무) 연속형 2개 이상 Levene 검정 등분산성 검정
평균 비교 연속형 2개 t-검정 그룹 간 평균 차이
평균 비교 연속형 3개 이상 ANOVA 다집단 평균 비교

14.7 자주 하는 오해와 주의사항

흔한 오해

오해 진실
“p < 0.05이면 강한 관계” p-value는 관계의 존재 여부만 판단, 강도는 별도 측정 필요
“카이제곱은 평균을 비교한다” 카이제곱은 빈도를 비교하는 검정
“카이제곱은 모든 데이터에 적용 가능” 표본 크기와 기대 빈도 조건이 중요
“독립성 검정에서 귀무가설 채택 = 변수가 독립” 귀무가설 기각 실패 ≠ 독립 증명

주의사항

  1. 기대 빈도 확인: 기대 빈도가 5 미만인 셀이 많으면 검정 신뢰도 저하
  2. 표본 크기: 표본이 너무 크면 실무적으로 의미 없는 차이도 유의하게 나옴
  3. 연관성 강도: p-value만으로 연관성 강도를 판단하지 말 것
  4. 인과관계: 독립성 검정은 상관관계만 확인, 인과관계는 아님
  5. 다중 비교: 여러 검정을 동시에 수행하면 1종 오류 증가

14.8 요약

이 장에서는 범주형 데이터의 빈도와 구조를 분석하는 카이제곱 검정을 학습했다. 주요 내용은 다음과 같다.

검정 유형별 요약

검정 목적 비교 대상 주요 사용
적합성 검정 분포가 이론과 일치하는가? 관측 분포 vs 이론 분포 샘플링 검증, 품질 관리
독립성 검정 두 변수가 관련 있는가? 변수 간 연관성 변수 관계 탐색
F 검정 두 분산이 같은가? 집단 간 분산 이론 학습 (실무는 Levene)

카이제곱 검정 핵심

  • 원리: 관측 빈도와 기대 빈도의 차이를 측정
  • 통계량: \(\chi^2 = \sum \frac{(O - E)^2}{E}\)
  • 조건: 기대 빈도가 모든 셀에서 5 이상 권장
  • 해석: p-value는 관계 존재 여부, 강도는 Cramér’s V 등으로 측정

실무 체크리스트

다음 단계

  • 적합성 검정 → 분포의 이론적 타당성 확인
  • 독립성 검정 → 변수 간 관계 파악 후 추가 분석
  • 평균 비교 전 → 구조적 패턴 먼저 확인

카이제곱 검정은 범주형 데이터 분석의 기초이다. 빈도와 구조를 먼저 파악한 후, 필요에 따라 평균 비교나 회귀 분석 등의 고급 기법으로 진행하는 것이 바람직하다. 다음 장에서는 평균 비교를 위한 t-검정과 ANOVA를 학습할 것이다.